package com.link2room.rodimus.facade.impl;

import com.link2room.common.constant.DefaultValue;
import com.link2room.common.dto.param.base.BaseL2rParamDto;
import com.link2room.common.dto.param.base.BaseParamDto;
import com.link2room.common.dto.result.base.MemberSessionDto;
import com.link2room.common.dto.threadlocal.ApiThreadLocal;
import com.link2room.common.enums.BaseSysExceptionEnum;
import com.link2room.common.exception.SysException;
import com.link2room.common.jpa.result.QueryResult;
import com.link2room.common.mns.MNSUtil;
import com.link2room.common.service.ICommonService;
import com.link2room.common.util.ClassConverUtil;
import com.link2room.common.util.DateUtil;
import com.link2room.common.util.JacksonUtil;
import com.link2room.common.util.L2RAssert;
import com.link2room.rodimus.constant.RodimusMnsConstant;
import com.link2room.rodimus.dto.param.api.SignInCalendarApiParamDto;
import com.link2room.rodimus.dto.param.api.SignInDrawAwardParamDto;
import com.link2room.rodimus.dto.param.api.SignInReissueParamDto;
import com.link2room.rodimus.dto.param.base.*;
import com.link2room.rodimus.entity.*;
import com.link2room.rodimus.facade.ISignInApiFacadeService;
import com.link2room.rodimus.factory.SignInDrawAwardHandlerFactory;
import com.link2room.rodimus.handler.ISignInDrawAwardHandler;
import com.link2room.rodimus.service.ISignInService;
import com.link2room.rodimus.util.CalendarUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.link2room.enums.exception.RodimusSysExceptionEnum.*;

/**
 * Created by LN on 2018/1/3
 */
@Component("signInApiFacadeService")
public class SignInApiFacadeServiceImpl implements ISignInApiFacadeService {
    @Resource
    private ICommonService commonService;
    @Resource
    private ISignInService signInService;
    @Resource
    private MNSUtil mnsUtil;


    @Override
    public void doSignNowDay() throws Exception {
        Date nowDay = CalendarUtils.getDateWithOutTime(LocalDate.now());
        L2RAssert.isTrue(_isSign(nowDay), new SysException(SIGN_IN_ERROR, "今日已经签到"));
        SignInDetailEntity signInDetail = doSignIn(nowDay);
        this.putDistributeAwardMessagetoQueue(ClassConverUtil.copyProperties(signInDetail, SignInDetailDto.class));
    }

    @Override
    public String isSign() throws Exception {
        Date nowDay = CalendarUtils.getDateWithOutTime(LocalDate.now());
        return _isSign(nowDay) ? DefaultValue.T : DefaultValue.F;
    }

    @Override
    public QueryResult<SignInCalendarDto> signInCalendar(SignInCalendarApiParamDto paramDto) throws Exception {
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        L2RAssert.isTrue(null == memberSession, new SysException(SIGN_IN_CALENDAR, "请登录."));
        L2RAssert.isTrue(DefaultValue.F.equals(memberSession.getIsMember()), new SysException(SIGN_IN_CALENDAR, "请先注册会员."));
        String month = paramDto.getMonth();
        Date dateMonth = DateUtil.parseString(month, "yyyy-MM");
        Date monthFirstDay = CalendarUtils.getStartTimeOfMonth(dateMonth);
        Date monthLastDay = CalendarUtils.getEndTimeOfMonth(dateMonth);
        QueryResult<SignInDetailEntity> details = signInService.signInDetail(dateMonth, memberSession.getLhotelGroupCode(), memberSession.getMemberNo());
        List<Date> dates = DateUtil.dateBewteen(monthFirstDay, monthLastDay);
        List<SignInCalendarDto> signIncalendars = dates.stream()
                .map(date -> new SignInCalendarDto(date, DefaultValue.F))
                .collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(details.getResultlist())) {
            Map<Date, List<SignInDetailEntity>> map = details.getResultlist()
                    .stream()
                    .collect(Collectors.groupingBy(SignInDetailEntity::getSignInDate));
            signIncalendars.forEach(signDay -> {
                if (map.get(signDay.getDate()) != null) {
                    signDay.setIsSigned(DefaultValue.T);
                }
            });
        }
        QueryResult<SignInCalendarDto> res = new QueryResult<>();
        res.setResultlist(signIncalendars);
        res.setTotalrecord(dates.size());
        return res;

    }

    @Override
    public void reissue(SignInReissueParamDto paramDto) throws Exception {
        Date reissueDate = paramDto.getReissueDate();
        L2RAssert.isTrue(null == reissueDate, new SysException(SIGN_IN_REISSUE, "[reissueDate] is required."));
        L2RAssert.isTrue(!dateIsInCurrentMonth(reissueDate), new SysException(SIGN_IN_REISSUE, "只能补签当月活动."));
        Date nowDay = CalendarUtils.getDateWithOutTime(LocalDate.now());
        reissueDate = CalendarUtils.getDateWithOutTime(reissueDate);
        L2RAssert.isTrue(nowDay.compareTo(reissueDate) <= 0, new SysException(SIGN_IN_REISSUE, "请补签当天之前的日期."));
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        String memberNo = memberSession.getMemberNo();
        String lhotelGroupCode = memberSession.getLhotelGroupCode();

        Date month = CalendarUtils.getStartTimeOfMonth(nowDay);
        SignInReissueBaseEntity find = new SignInReissueBaseEntity();
        find.setLhotelGroupCode(lhotelGroupCode);
        find.setMemberNo(memberNo);
        find.setMonth(month);
        SignInReissueBaseEntity qres = commonService.findOnlyOneEQ(SignInReissueBaseEntity.class, find);
        L2RAssert.isTrue(null == qres, new SysException(SIGN_IN_REISSUE, "当月没有补签机会."));
        L2RAssert.isTrue(qres.getUsed() >= qres.getTotal(), new SysException(SIGN_IN_REISSUE, "当月没有补签机会."));
        L2RAssert.isTrue(_isSign(reissueDate), new SysException(SIGN_IN_REISSUE, "已经签到不能补签."));
        SignInDetailEntity signInDetail = signInService.reissue(lhotelGroupCode, reissueDate, memberNo);

        this.putDistributeAwardMessagetoQueue(ClassConverUtil.copyProperties(signInDetail, SignInDetailDto.class));
    }

    @Override
    public SignInCurrentUserReissueTimesDto signInCurrentUserReissueTimes() throws Exception {
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        L2RAssert.isTrue(null == memberSession, new SysException(SIGN_IN_CALENDAR, "请登录."));
        L2RAssert.isTrue(DefaultValue.F.equals(memberSession.getIsMember()), new SysException(SIGN_IN_CALENDAR, "请先注册会员."));
        String lhotelGroupCode = memberSession.getLhotelGroupCode();
        String memberNo = memberSession.getMemberNo();

        SignInReissueBaseEntity find = new SignInReissueBaseEntity();
        find.setLhotelGroupCode(lhotelGroupCode);
        find.setMemberNo(memberNo);
        find.setMonth(CalendarUtils.getStartTimeOfMonth(new Date()));
        SignInReissueBaseEntity qres = commonService.findOnlyOneEQ(SignInReissueBaseEntity.class, find);
        SignInCurrentUserReissueTimesDto timesDto = new SignInCurrentUserReissueTimesDto();
        if (null == qres) {
            timesDto.setReissueTimes(0);
        } else {
            int reissueTimes = qres.getTotal() - qres.getUsed();
            timesDto.setReissueTimes(reissueTimes >= 0 ? reissueTimes : 0);
        }
        return timesDto;
    }

    @Override
    public QueryResult<SignInAwardDto> currentAwards(BaseParamDto paramDto) throws Exception {
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        L2RAssert.isTrue(null == memberSession, new SysException(SIGN_IN_ERROR, "请登录."));
        L2RAssert.isTrue(DefaultValue.F.equals(memberSession.getIsMember()), new SysException(SIGN_IN_ERROR, "请先注册会员."));
        String lhotelGorupCode = paramDto.getLhotelGroupCode();
        L2RAssert.stringBlank(lhotelGorupCode, "[lhotelGroupCode] is required.");
        String memberNo = memberSession.getMemberNo();
        SignInBaseDto qres = signInService.getUnderwaySignBaseWithAwardInfo(lhotelGorupCode);
        Date signInDateMonth = CalendarUtils.getStartTimeOfMonth(new Date());
        if (null == qres || CollectionUtils.isEmpty(qres.getSignInAwardBases())) {
            QueryResult<SignInAwardDto> res = new QueryResult<>();
            res.setTotalrecord(0);
            res.setCurrentPage(0);
            return res;
        } else {
            List<SignInAwardDto> awards = qres.getSignInAwardBases().stream()
                    .filter(awardBaseDto -> !(signInService.isEveryAward(awardBaseDto.getDayNum(), awardBaseDto.getCouldLoopDraw())))
                    .map(awardBaseDto -> {
                        SignInAwardDto awardDto = new SignInAwardDto();
                        awardDto.setCode(awardBaseDto.getCode());
                        awardDto.setName(awardBaseDto.getDescription());
                        try {
                            SignInAwardDrawDetailEntity find = new SignInAwardDrawDetailEntity();
                            find.setAwardCode(awardBaseDto.getCode());
                            find.setLhotelGroupCode(paramDto.getLhotelGroupCode());
                            find.setMonth(signInDateMonth);
                            find.setMemberNo(memberNo);
                            long total = commonService.getCountAllEQ(SignInAwardDrawDetailEntity.class, find, null);
                            find.setHasDrawn(DefaultValue.T);
                            long drawCount = commonService.getCountAllEQ(SignInAwardDrawDetailEntity.class, find, null);

                            String couldDraw = (total > 0) ? DefaultValue.T : DefaultValue.F;
                            String hasDraw = null;
                            if(DefaultValue.F.equals(couldDraw)){
                                hasDraw = DefaultValue.F;
                            }else {
                                hasDraw = drawCount < total ? DefaultValue.F : DefaultValue.T;
                            }
                            awardDto.setCouldDraw(couldDraw);
                            awardDto.setHasDrawn(hasDraw);
                        } catch (Exception e) {
                            throw new SysException(BaseSysExceptionEnum.BASE_FUCATION_OPTION, e);
                        }
                        return awardDto;

                    }).collect(Collectors.toList());
            QueryResult<SignInAwardDto> res = new QueryResult<>();
            res.setTotalrecord(awards.size());
            res.setCurrentPage(1);
            res.setResultlist(awards);
            return res;
        }
    }

    @Override
    public SignInAwardDto currentEveryDayAwards(BaseParamDto paramDto) throws Exception {
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        L2RAssert.isTrue(null == memberSession, new SysException(SIGN_IN_ERROR, "请登录."));
        L2RAssert.isTrue(DefaultValue.F.equals(memberSession.getIsMember()), new SysException(SIGN_IN_ERROR, "请先注册会员."));
        String lhotelGorupCode = paramDto.getLhotelGroupCode();
        L2RAssert.stringBlank(lhotelGorupCode, "[lhotelGroupCode] is required.");
        SignInBaseDto qres = signInService.getUnderwaySignBaseWithAwardInfo(lhotelGorupCode);
        if (null == qres || CollectionUtils.isEmpty(qres.getSignInAwardBases())) {
            return null;
        } else {
            Optional<SignInAwardDto> award = qres.getSignInAwardBases().stream()
                    .filter(awardBaseDto -> signInService.isEveryAward(awardBaseDto.getDayNum(), awardBaseDto.getCouldLoopDraw()))
                    .map(awardBaseDto -> {
                        SignInAwardDto awardDto = new SignInAwardDto();
                        awardDto.setCode(awardBaseDto.getCode());
                        awardDto.setName(awardBaseDto.getDescription());
                        return awardDto;
                    }).findFirst();
            return award.orElse(null);
        }
    }

    @Override
    public void drawAward(SignInDrawAwardParamDto awardParamDto) throws Exception {
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        L2RAssert.isTrue(null == memberSession, new SysException(SIGN_IN_DRAW_AWARD, "请登录."));
        L2RAssert.isTrue(DefaultValue.F.equals(memberSession.getIsMember()), new SysException(SIGN_IN_DRAW_AWARD, "请先注册会员."));
        String lhotelGroupCode = awardParamDto.getLhotelGroupCode();
        String memberNo = memberSession.getMemberNo();
        String code = awardParamDto.getCode();

        SignInAwardBaseEntity findAwardBase = new SignInAwardBaseEntity();
        findAwardBase.setCode(code);
        findAwardBase.setLhotelGroupCode(lhotelGroupCode);
        SignInAwardBaseEntity awardBase = commonService.findOnlyOneEQ(SignInAwardBaseEntity.class, findAwardBase);
        L2RAssert.isTrue(null == awardBase, new SysException(SIGN_IN_DRAW_AWARD, "奖励不存在[awardCode] is " + code));

        Date signInDateMonth = CalendarUtils.getStartTimeOfMonth(new Date());
        SignInAwardDrawDetailEntity find = new SignInAwardDrawDetailEntity();
        find.setAwardCode(code);
        find.setLhotelGroupCode(lhotelGroupCode);
        find.setMonth(signInDateMonth);
        find.setMemberNo(memberNo);
        find.setHasDrawn(DefaultValue.F);
        SignInAwardDrawDetailEntity qresD = commonService.findOneEQ(SignInAwardDrawDetailEntity.class, find);
        L2RAssert.isTrue(null == qresD, new SysException(SIGN_IN_DRAW_AWARD, "没有领奖次数.[memberNo] " + memberNo + "[awardCode] " + code));
        qresD.setHasDrawn(DefaultValue.T);
        commonService.merge(qresD);

        String type = awardBase.getType();
        String value = awardBase.getValue();

        ISignInDrawAwardHandler handler = SignInDrawAwardHandlerFactory.getHandler(type);
        try {
            handler.drawAward(lhotelGroupCode, type, value);
        } catch (Exception e) {
            qresD.setHasDrawn(DefaultValue.F);
            commonService.merge(qresD);
            throw e;
        }
    }

    @Override
    public SignInBaseDto currentActivityInfo(BaseL2rParamDto paramDto) throws Exception {
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        return signInService.getUnderwaySignInBaseAll(lhotelGroupCode);
    }

    @Override
    public QueryResult<SignInReissueDetailShareDto> currentUserShareDetails() throws Exception {
        BaseParamDto param = new BaseParamDto();
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        L2RAssert.isTrue(null == memberSession, new SysException(SIGN_IN_ERROR, "请登录."));
        L2RAssert.isTrue(DefaultValue.F.equals(memberSession.getIsMember()), new SysException(SIGN_IN_ERROR, "请先注册会员."));
        String lhotelGroupCode = memberSession.getLhotelGroupCode();
        String memberNo = memberSession.getMemberNo();
        Date monthDate = CalendarUtils.getStartTimeOfMonth(new Date());
        SignInReissueDetailShareEntity find = new SignInReissueDetailShareEntity();
        find.setLhotelGroupCode(lhotelGroupCode);
        find.setFromMember(memberNo);
        find.setMonth(monthDate);
        QueryResult<SignInReissueDetailShareEntity> qres = commonService.findAllEQ(SignInReissueDetailShareEntity.class, find, param.getCurrentPage(), param.getPageSize());
        return ClassConverUtil.copyQueryResultToDtoQueryResult(qres, SignInReissueDetailShareDto.class, false);
    }

    private boolean dateIsInCurrentMonth(Date date) {
        Date dateMonth = new Date();
        Date monthFirstDay = CalendarUtils.getStartTimeOfMonth(dateMonth);
        Date monthLastDay = CalendarUtils.getEndTimeOfMonth(dateMonth);
        return monthFirstDay.compareTo(date) <= 0 && monthLastDay.compareTo(date) >= 0;
    }

    private SignInDetailEntity doSignIn(Date signDate) throws Exception {
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        String memberNo = memberSession.getMemberNo();
        String lhotelGroupCode = memberSession.getLhotelGroupCode();
        SignInDetailEntity newSign = new SignInDetailEntity();
        newSign.setSignInDate(signDate);
        newSign.setLhotelGroupCode(lhotelGroupCode);
        newSign.setMemberNo(memberNo);
        newSign.setIsReissue(DefaultValue.F);
        commonService.save(newSign);
        return newSign;
    }

    private boolean _isSign(Date signDate) throws Exception {
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        L2RAssert.isTrue(null == memberSession, new SysException(SIGN_IN_ERROR, "请登录."));
        L2RAssert.isTrue(DefaultValue.F.equals(memberSession.getIsMember()), new SysException(SIGN_IN_ERROR, "请先注册会员."));
        String memberNo = memberSession.getMemberNo();
        L2RAssert.stringBlank(memberNo, "[memberNo] is null.error session");
        SignInDetailEntity find = new SignInDetailEntity();
        find.setMemberNo(memberNo);
        find.setSignInDate(signDate);
        long qres = commonService.getCountAllEQ(SignInDetailEntity.class, find, new String[]{SignInDetailEntity.BaseEntity_.lhotelGroupCode.toString()});
        return qres > 0;
    }

    private void putDistributeAwardMessagetoQueue(SignInDetailDto signInDetailDto) {
        //奖励分发
        SignDistributeAwardParamDto signDistributeAwardParamDto = new SignDistributeAwardParamDto();
        signDistributeAwardParamDto.setSignInDetailDto(signInDetailDto);
        mnsUtil.putMessageToQueue(RodimusMnsConstant.QUEUE_SIGN_IN_DISTRIBUTE_AWARD, JacksonUtil.beanToJson(signDistributeAwardParamDto));
    }
}